package bll

import (
	"auth/authentication"
	"auth/client/grpc"
	log "auth/collector/logger"
	"auth/errors"
	"auth/lib/token"
	"auth/store/postgres"
	"auth/store/redis"
	"auth/utils"
	"context"
	"gorm.io/gorm"
	"proto/dto"
	"proto/manager"
	"time"

	"auth/event"
	"auth/model"
	"auth/model/entity"
	"auth/store"
)

const (
	NORMAL = iota
	DISABLED
	DELETED
)

type user struct {
	iUser          store.IUser
	iUserAuthority store.IUserAuthority
	iUserJob       store.IUserJob
	iDept          store.IDept
	iAuthority     store.IAuthority
	iJob           store.IJob
	iUserRedis     store.IUserRedis
}

var User = &user{}

func (a *user) init() func() {
	a.iUser = postgres.User
	a.iUserAuthority = postgres.UserAuthority
	a.iUserJob = postgres.UserJob
	a.iDept = postgres.Dept
	a.iAuthority = postgres.Authority
	a.iJob = postgres.Job
	a.iUserRedis = redis.UserRedis
	return func() {}
}

func (a *user) onEvent(*event.Data) {}

// Create 创建用户
func (a *user) Create(ctx context.Context, in *model.UserCreateRequest) error {
	var (
		err    error
		userID int64
	)
	d, err := a.iUser.FindByParameter(&model.UserInfo{Phone: in.Phone})
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}
	if d.ID > 0 {
		return errors.UserAlreadyExist.Error()
	}
	if in.Status == nil {
		return errors.ParameterErr.Error()
	}
	c := buildUser(in)
	c.Password = utils.EncryptionSha256(in.Password, c.Salt)
	userID, err = a.iUser.Create(c)
	if err != nil {
		log.Error(err)
		return err
	}
	err = a.iUserAuthority.SetUserAuthorities(userID, in.AuthorityIds)
	if err != nil {
		log.Error("Create.SetUserAuthorities", err)
		return err
	}
	if len(in.JobIds) != 0 {
		err = a.iUserJob.SetUserJobs(userID, in.JobIds)
	}
	return err
}

func (a *user) IsDisabled(in *model.IsDisabledRequest) error {
	var (
		err  error
		u    *entity.User
		dict = make(map[string]interface{})
	)
	if in.Status == nil {
		return errors.ParameterErr.Error()
	}
	u, err = a.iUser.Find(in.ID)
	if err != nil || errors.Is(err, gorm.ErrRecordNotFound) {
		log.Error(err)
		return errors.UserNotExist.Error()
	}
	dict["status"] = in.Status
	return a.iUser.Update(u.ID, dict)
}

func (a *user) ResetPassword(in *model.ResetPasswordRequest) error {
	var (
		err  error
		u    *entity.User
		dict = make(map[string]interface{})
	)
	u, err = a.iUser.Find(in.ID)
	if err != nil || errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.UserNotExist.Error()
	}
	dict["password"] = utils.EncryptionSha256(in.Password, u.Salt)
	return a.iUser.Update(u.ID, dict)
}

func (a *user) FindSysById(in *model.FindSysByIdRequest) (*model.FindSysByIdResponse, error) {
	var (
		out        = &model.FindSysByIdResponse{}
		authoritys []*entity.Authority
		jobs       []*entity.Job
	)
	u, err := a.iUser.Find(in.ID)
	if err != nil || errors.Is(err, gorm.ErrRecordNotFound) {
		log.Error(err)
		return nil, errors.NoData.Error()
	}
	out.UserInfo = model.UserEntityToDto(u)
	d, err := a.iDept.FindByParameter(&model.DeptInfo{
		DeptId: u.DeptId,
	})
	if err != nil {
		return nil, err
	}
	if len(d.DeptName) > 0 {
		out.UserInfo.DeptName = d.DeptName
	}
	authoritys, err = a.iAuthority.FindByUserId(in.ID)
	if err != nil {
		return nil, err
	}
	if len(authoritys) > 0 {
		out.Authorities = model.AuthoritysEntityToDto(authoritys)
	}
	jobs, err = a.iJob.FindByUserId(in.ID)
	if err != nil {
		return nil, err
	}
	if len(jobs) > 0 {
		out.Jobs = model.JobsEntityToDto(jobs)
	}
	return out, err
}

func (a *user) Update(ctx context.Context, in *model.UserUpdateRequest) error {
	var (
		dict = make(map[string]interface{})
		err  error
	)
	err = a.iUserAuthority.SetUserAuthorities(in.ID, in.AuthorityIds)
	if err != nil {
		log.Error("Update.SetUserAuthorities", err)
		return err
	}
	err = a.iUserJob.SetUserJobs(in.ID, in.JobIds)
	if err != nil {
		log.Error("Update.SetUserJobs", err)
		return err
	}
	if in.Name != nil {
		dict["name"] = in.Name
	}
	if in.Status != nil {
		dict["status"] = in.Status
	}
	if in.Picture != nil {
		dict["picture"] = in.Picture
	}
	dict["dept_id"] = in.DeptId
	return a.iUser.Update(in.ID, dict)
}

func (a *user) Delete(ctx context.Context, in *model.UserDeleteRequest) error {
	return a.iUser.Delete(in.ID)
}

func (a *user) List(ctx context.Context, in *model.UserListRequest) (*model.UserListResponse, error) {
	var (
		err   error
		total int64
		list  = make([]*entity.User, 0)
		out   = &model.UserListResponse{}
	)
	in.Size, in.Index = utils.PageUtil(in.Size, in.Index)

	if total, list, err = a.iUser.List(in); err != nil {
		return out, err
	}

	out.Total = total
	out.Data = model.UsersEntityToInfoResponse(list)
	// 传递user ID 查询车辆数量
	// 创建一个新的切片
	if l := len(list); l > 0 {
		type EbikeCount struct {
			ID    int64 `json:"id"`
			Count int64 `json:"count"`
		}

		// 用户ID组装
		userIds := make([]int64, l)
		for i, u := range list {
			userIds[i] = u.ID
		}

		deviceCountByUser, err := grpc.EbikeCmd.DeviceCountByUser(ctx, &dto.CountDeviceByIdsRequest{Ids: userIds})
		if err != nil {
			log.Error(err)
			return nil, errors.UserQueryFailed.Warp(err, errors.UserQueryFailed.String())
		}

		//解析数据
		for i, datum := range out.Data {
			for _, count := range deviceCountByUser.DeviceCounts {
				if datum.ID == count.Id {
					out.Data[i].DeviceCount = int(count.Count)
				}
			}
		}
	}

	return out, nil
}

// GetSysUserList 系统用户分页列表
func (a *user) GetSysUserList(in *model.GetSysUserListRequest) (*model.GetSysUserListResponse, error) {
	var (
		err           error
		total         int64
		list          = make([]*model.GetSysUserList, 0)
		out           = &model.GetSysUserListResponse{}
		deptIds       []int64
		authorityList []*entity.Authority
	)
	in.Size, in.Index = utils.PageUtil(in.Size, in.Index)
	if in.DeptId > 0 {
		deptIds, err = a.iDept.GetChildrenByDeptId(in.DeptId)
		if err != nil {
			log.Error(err)
			return nil, err
		}
	}
	if total, list, err = a.iUser.GetSysUserList(in, deptIds); err != nil {
		return out, err
	}
	if len(list) > 0 {
		for _, userList := range list {
			authorityList, err = a.iAuthority.FindByUserId(userList.ID)
			if err != nil {
				return nil, err
			}
			userList.Authorities = model.AuthoritysEntityToDto(authorityList)
		}
	}
	out.Total = total
	out.Data = list
	return out, err
}

func (a *user) DeleteSysUser(in *model.UserDeleteRequest) error {
	// 这里先检查用户身上有没有绑定车辆，如果有则提示要求先解绑才能删除用户
	var err error
	bikes, err := grpc.EbikeCmd.GetUserEbikeList(context.Background(), &manager.GetUserEbikeListReq{UserId: in.ID})
	if bikes.Total != 0 {
		return errors.UserHasBike.Error()
	}
	if err = a.iUser.Delete(in.ID); err != nil {
		return err
	}
	if err = a.iUserJob.DeleteByUserId(in.ID); err != nil {
		return err
	}
	return a.iUserAuthority.DeleteByUserId(in.ID)
}

func (a *user) FindById(id int64) (*model.UserInfoResponse, error) {
	var (
		err    error
		out    *model.UserInfoResponse
		entity = &entity.User{}
	)
	if id <= 0 {
		// 错误处理
		return out, err
	}

	if entity, err = a.iUser.Find(id); err != nil {
		return out, err
	}

	out = &model.UserInfoResponse{
		ID:        entity.ID,
		Phone:     entity.Phone,
		Name:      entity.Name,
		Status:    entity.Status,
		RoleIds:   entity.RoleIds,
		Picture:   entity.Picture,
		Type:      convertUserType(entity.Type),
		WxID:      entity.WxID,
		AppleID:   entity.AppleID,
		CreatedAt: entity.CreatedAt,
	}

	return out, err
}

func convertUserType(u string) []string {
	var (
		uType []string
	)
	for i := 0; i < 3; i++ {
		var roleName string
		switch i {
		case 0:
			roleName = "普通用户"
		case 1:
			roleName = "经销商用户"
		default:
			roleName = "后台管理用户"
		}
		if u[i] == '1' {
			uType = append(uType, roleName)
		}
	}
	return uType
}

// FindEntityById 返回用户所有信息用于鉴权
func (a *user) FindEntityById(id int64) (*model.UserInfo, error) {
	var (
		err    error
		out    *model.UserInfo
		entity = &entity.User{}
	)
	if id <= 0 {
		// 错误处理
		return out, err
	}

	if entity, err = a.iUser.Find(id); err != nil {
		return out, err
	}

	out = &model.UserInfo{
		ID:        entity.ID,
		Phone:     entity.Phone,
		Name:      entity.Name,
		WxID:      entity.WxID,
		AppleID:   entity.AppleID,
		Password:  entity.Password,
		Salt:      entity.Salt,
		Status:    entity.Status,
		RoleIds:   entity.RoleIds,
		Picture:   entity.Picture,
		Type:      entity.Type,
		CreatedAt: entity.CreatedAt,
		UpdatedAt: entity.UpdatedAt,
	}

	return out, err
}
func (a *user) FindUserByPhone(phone string) (out model.UserInfo, err error) {
	loginUser, err := a.iUser.FindUserByPhone(phone)
	out = model.UserInfo{
		ID:        loginUser.ID,
		Phone:     loginUser.Phone,
		Name:      loginUser.Name,
		WxID:      loginUser.WxID,
		AppleID:   loginUser.AppleID,
		Password:  loginUser.Password,
		Salt:      loginUser.Salt,
		Status:    loginUser.Status,
		RoleIds:   loginUser.RoleIds,
		Picture:   loginUser.Picture,
		Type:      loginUser.Type,
		CreatedAt: loginUser.CreatedAt,
		UpdatedAt: loginUser.UpdatedAt,
	}
	return
}

// FindByParameter 多条件查询
func (a *user) FindByParameter(request *model.UserInfo) (out *model.UserInfo, err error) {
	var (
		entity = &entity.User{}
	)

	out = &model.UserInfo{}

	if entity, err = a.iUser.FindByParameter(request); err != nil {
		return out, err
	}

	out = &model.UserInfo{
		ID:        entity.ID,
		Phone:     entity.Phone,
		Name:      entity.Name,
		WxID:      entity.WxID,
		AppleID:   entity.AppleID,
		Password:  entity.Password,
		Salt:      entity.Salt,
		Status:    entity.Status,
		RoleIds:   entity.RoleIds,
		Picture:   entity.Picture,
		Type:      entity.Type,
		CreatedAt: entity.CreatedAt,
		UpdatedAt: entity.UpdatedAt,
	}

	return out, err
}

func (a *user) UserListByIds(ctx context.Context, in dto.UserListInfoRequest) (out dto.UserListInfoResponse, err error) {
	return a.iUser.UserListByIds(in)
}

func (a *user) Login(ctx context.Context, in *model.UserLoginRequest) (*model.UserLoginResponse, error) {
	var (
		err          error
		userToken    *token.Token
		userInfo     model.UserInfo
		ua           []*entity.UserAuthority
		authorityIds = make([]int64, 0)
	)

	// 判断是否注册,用户类型
	userInfo, err = a.FindUserByPhone(in.Phone)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	if errors.Is(err, gorm.ErrRecordNotFound) || userInfo.ID == 0 || userInfo.Status == DELETED {
		// 用户不存在
		return nil, errors.UserNotExist.Error()
	}
	// 用户被禁用
	if userInfo.Status == DISABLED {
		return nil, errors.UserDisabled.Error()
	}
	if userInfo.Type != string(model.Admin) && userInfo.Type != string(model.AppAndAdmin) {
		// 没有管理层权限
		return nil, errors.NotPermission.Error()
	}

	// 验证密码
	/*	b, err := authentication.PasswordCheck(in.Password, userInfo.Salt, userInfo.Password)

		if !b {
			return nil, err // 密码不正确
		}*/

	//
	pw := utils.EncryptionSha256(in.Password, userInfo.Salt)
	if pw != userInfo.Password {
		return nil, errors.PasswordIncorrect.Error()
	}
	ua, err = a.iUserAuthority.FindByUserId(userInfo.ID)
	if err != nil {
		return nil, err
	}
	if len(ua) > 0 {
		for _, e := range ua {
			authorityIds = append(authorityIds, e.AuthorityId)
		}
	}
	//返回token
	userToken, err = authentication.Generate(&authentication.Options{
		UserID: userInfo.ID,
		Roles:  authorityIds,
	}, model.TokenExpTime)

	if err != nil {
		return nil, err
	}
	out := &model.UserLoginResponse{
		Token:     userToken.Token,
		Phone:     userInfo.Phone,
		Name:      userInfo.Name,
		Status:    userInfo.Status,
		Picture:   userInfo.Picture,
		RoleIds:   authorityIds,
		RoleNames: []string{},
	}

	return out, err
}

func (a *user) SetUserAuthorities(in *model.SetUserAuthoritiesRequest) (err error) {
	return a.iUserAuthority.SetUserAuthorities(in.ID, in.AuthorityIds)
}

func (a *user) ChangePassword(in *model.ChangePasswordRequest) error {
	var (
		err  error
		u    *entity.User
		dict = make(map[string]interface{})
	)
	u, err = a.iUser.Find(in.ID)
	if err != nil || errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.UserNotExist.Error()
	}
	if u.Password != (utils.EncryptionSha256(in.OldPwd, u.Salt)) {
		return errors.PasswordIncorrect.Error()
	}
	dict["password"] = utils.EncryptionSha256(in.Password, u.Salt)
	return a.iUser.Update(u.ID, dict)
}

func buildUser(in *model.UserCreateRequest) *entity.User {
	var arr []int64
	if in.RoleIds == nil {
		arr = []int64{}
	}
	return &entity.User{

		Phone: in.Phone,

		Name: in.Name,

		WxID: in.WxID,

		AppleID: in.AppleID,

		Password: in.Password,

		Salt: utils.RandString(16),

		Status: *in.Status,

		RoleIds: arr,

		Picture: in.Picture,

		Type: in.Type,

		DeptId: in.DeptId,

		CreatedAt: time.Now().Unix(),

		UpdatedAt: time.Now().Unix(),
	}
}
